package org.game.module.buildingbo;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;

import org.apache.camel.component.properties.SysPropertiesFunction;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.cfg.BuildCfg;
import org.cfg.CityCfg;
import org.cfg.DiscreteDataCfg;
import org.cfg.GashaponCfg;
import org.cfg.cache.BuildCfgCache;
import org.cfg.cache.CityCfgCache;
import org.cfg.cache.DiscreteDataCfgCache;
import org.cfg.cache.EggCfgCache;
import org.cfg.cache.GashaponCfgCache;
import org.constant.DiscreteDataID;
import org.constant.DiscreteDataKey;
import org.constant.EventConditionKey;
import org.constant.EventConditionType;
import org.constant.EventType;
import org.constant.ItemConstantId;
import org.constant.smallkey.WorkLogSmallKey;
import org.error.GameErrorCode;
import org.game.constant.BuildingConstant;
import org.game.constant.SessionConstent;
import org.game.constant.SmallGameConstant;
import org.game.db.bean.*;
import org.game.db.cache.BuildingCache;
import org.game.db.cache.CityCache;
import org.game.db.cache.DungeonLevelCache;
import org.game.db.cache.RoleCache;
import org.game.db.proxy.bean.PlayerDataProxy;
import org.game.db.vo.PlayerDataVO.DiscreteVO;
import org.game.db.vo.PlayerDataVO.GashaponVO;
import org.game.log.LogBuilding;
import org.game.log.builder.LogDsp;
import org.game.log.dsp.BuildingDsp;
import org.game.log.dsp.GoodsDsp;
import org.game.manager.BuildingManager;
import org.game.manager.ItemManager;
import org.game.manager.ItemPackageHelper;
import org.game.msgBuilder.BuildingMsgBuilder;
import org.game.plugin.event.GameEvent;
import org.game.plugin.event.GameEventPlugin;
import org.game.protobuf.c2s.C2SBuildingMsg;
import org.game.protobuf.c2s.C2SBuildingMsg.GetAllUnlockBuildingMsg;
import org.game.protobuf.c2s.C2SBuildingMsg.GetWorkRewardMsg;
import org.game.protobuf.c2s.C2SBuildingMsg.ReqCheckGashaponResult;
import org.game.protobuf.c2s.C2SBuildingMsg.ReqGetGashaponInfo;
import org.game.protobuf.c2s.C2SBuildingMsg.ReqRefreshGashaponPool;
import org.game.protobuf.c2s.C2SBuildingMsg.ReqStartGashapon;
import org.game.protobuf.c2s.C2SBuildingMsg.ReqUpgradeFul;
import org.game.protobuf.c2s.C2SBuildingMsg.UnlockBuildingMsg;
import org.game.protobuf.c2s.C2SBuildingMsg.WorkMsg;
import org.game.protobuf.s2c.S2CBuildingMsg;
import org.game.protobuf.s2c.S2CBuildingMsg.BuildingInfo;
import org.game.protobuf.s2c.S2CBuildingMsg.UpdateBuildingInfo;
import org.game.protobuf.s2c.S2CItemMsg;
import org.game.protobuf.s2c.S2CShareMsg.RewardsMsg;
import org.game.util.ProtoUnit;
import org.utils.CommonUtil;

import com.alibaba.fastjson.JSONArray;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.sojoys.artifact.exception.AbstractLogicModelException;
import com.sojoys.artifact.factory.session.ISession;
import com.sojoys.artifact.manager.MessageManager;
import com.sojoys.artifact.tools.ToolDate;
import com.sojoys.artifact.tools.ToolError;
import com.sojoys.artifact.tools.ToolMap;
import com.xiaoleilu.hutool.util.MapUtil;

public class BuildingBOImpl implements BuildingBO {

    @Override
    public byte[] getBuildingInfo(ISession session, GetAllUnlockBuildingMsg msg)
            throws AbstractLogicModelException {
        Player player = session.getAttr(SessionConstent.PLAYER);
        List<Building> buildings = player.getBuildingsFkPlayerId();
        byte[] data = ProtoUnit.toByte(BuildingMsgBuilder.buildBuildingInfoMsg(buildings));
        return data;
    }

    @Override
    public byte[] unlockBuilding(ISession session, UnlockBuildingMsg unlockBuilding)
            throws AbstractLogicModelException {
        Player player = session.getAttr(SessionConstent.PLAYER);
        List<Integer> buildingIds = unlockBuilding.getBuildingCidsList();

        S2CBuildingMsg.UnlockBuildingMsg.Builder msg =
                S2CBuildingMsg.UnlockBuildingMsg.newBuilder();

        for (Integer buildingId : buildingIds) {
            BuildCfg buildCfg = BuildCfgCache.me().getById(buildingId);
            // 各种校验
            checkBuildingIsUnlock(player, buildingId);
            checkBuildUnlockCondition(player, buildCfg);

            // 建筑CID
            Building building =
                    new Building(0L, buildingId, BuildingConstant.BUIDING_STATE_FREE, null, null,
                            player.getId(), Lists.newArrayList(), Maps.newHashMap(),
                            Lists.newArrayList(), false);
            building.insert();

            // 解锁city
            BuildingManager.unlockCity(player, building);


            // 事件
            Map<String, Object> in = Maps.newHashMap();
            in.put(EventConditionKey.CONDITION_TYPE, EventConditionType.BUILDING_LVL_COUNT);
            in.put(EventConditionKey.BUILD_CID, buildCfg.getBuildId());
            in.put(EventConditionKey.OLD_LEVEL, 0);
            in.put(EventConditionKey.NOW_LEVEL, buildCfg.getLevel());
            GameEvent event = new GameEvent(player, EventType.BUILDING_EVENT, in);
            GameEventPlugin.syncSubmit(event);

            BuildingInfo buildingInfo = BuildingMsgBuilder.buildBuildingInfoMsg(building);

            msg.addBuildingInfos(buildingInfo);

            LogDsp logDsp = LogDsp.newBuilder(BuildingDsp.UNLOCK_BUILDING);
            LogBuilding.me().log(player.getId(), buildCfg.getBuildId(), logDsp);
        }


        return ProtoUnit.toByte(msg.build());
    }

    @Override
    public byte[] work(ISession session, WorkMsg work) throws AbstractLogicModelException {
        Player player = session.getAttr(SessionConstent.PLAYER);

        int cid = work.getBuidingCid();

        List<Long> roleIds = new ArrayList<>(work.getRoleIdsList().size());
        work.getRoleIdsList().forEach(id -> {
            roleIds.add(Long.valueOf(id));
        });

        // 此方法已做非空校验
        checkBuildingIsNotUnlock(player, cid);

        Building building = BuildingCache.me().getByPlayerIdCid(player.getId(), cid);
        checkBuildingIsFree(building);

        checkRoleId(roleIds);

        checkWorkerNum(building, roleIds);

        // 校验hero是否处于打工状态
        checkRoleWorkState(player, roleIds);

        // 校验打工条件
        checkWorkRule(player, building, roleIds);

        building.getBuildingProxy().clear();

        // 添加打工日志
        Map<Integer, Object> log = Maps.newHashMap();
        log.put(WorkLogSmallKey.TYPE, 2);
        log.put(WorkLogSmallKey.TIME, (int) (System.currentTimeMillis() / ToolDate.TIME_SECOND));
        building.getEventLogs().add(log);
        building.update();
        BuildingMsgBuilder.pushWorkLog(player, cid, log);


        building.setState(BuildingConstant.BUIDING_STATE_WORK);
        building.getRoleId().addAll(roleIds);
        building.setFinishTime(getTargetTime(building.getBuildingProxy().getCfg().getWorkTime()));
        building.setNextRewardTime(getTargetTime(building.getBuildingProxy().getCfg().getCycle()));
        building.update();

        player.getPlayerProxy().startSecondsJob();

        // List<Hero> updateHeroList = Lists.newArrayList();
        roleIds.forEach(roleId -> {
            Role role = RoleCache.me().getById(roleId);
            role.setWorkBuildingId(building.getBuildingProxy().getBuildId());
            role.update();
            // updateHeroList.add(hero);
        });
        // player.sendMsg(MessageManager.me().create(HeroBO.sendHeros,
        // ProtoUnit.toByte(HeroMsgBuilder.createHeroInfoList(S2CShareMsg.ChangeType.UPDATE,
        // updateHeroList))));

        BuildingInfo buildingInfo = BuildingMsgBuilder.buildBuildingInfoMsg(building);
        org.game.protobuf.s2c.S2CBuildingMsg.WorkMsg msg =
                S2CBuildingMsg.WorkMsg.newBuilder().setBuildingInfo(buildingInfo).build();

        BuildCfg buildingCfg = building.getBuildingProxy().getNextLevelCfg();
        LogDsp logDsp =
                LogDsp.newBuilder(BuildingDsp.WORK).addAttr("nowLvl", buildingCfg.getLevel());
        LogBuilding.me().log(player.getId(), building.getCid(), logDsp);
        return ProtoUnit.toByte(msg);
    }

    /**
     * 建筑升级
     */
    @Override
    public byte[] upgrade(ISession session, C2SBuildingMsg.BuildingUpgradeMsg upgrade)
            throws AbstractLogicModelException {

        Player player = session.getAttr(SessionConstent.PLAYER);
        int cid = upgrade.getBuidingCid();
        Building building = BuildingCache.me().getByPlayerIdCid(player.getId(), cid);
        ToolError.isAndTrue(GameErrorCode.CLIENT_PARAM_IS_ERR, building == null);

        ToolError.isAndTrue(GameErrorCode.BUILDING_IS_NOT_FREE,
                BuildingConstant.BUIDING_STATE_FREE != building.getState());

        BuildCfg cfg = building.getBuildingProxy().getCfg();
        BuildCfg nextLevelCfg = building.getBuildingProxy().getNextLevelCfg();
        // 已达最高级
        ToolError.isAndTrue(GameErrorCode.BUILDING_IS_LEVEL_LIMIT, nextLevelCfg == null);
        // 检查建筑升级条件
        checkBuildingUpGradeCondition(player, cfg);
        // 道具不足
        ToolError.isAndTrue(GameErrorCode.ITEM_NOT_ENOUGH_CAN_NOT_UPGRADE_BUILD,
                !ItemManager.checkGoodsByCid(player, cfg.getConsume()));


        // 构建日志
        LogDsp log = LogDsp.newBuilder(GoodsDsp.BUILDING_UPGRADE).targetTag(cfg.getId());

        ItemManager.costGoodsByCid(player, cfg.getConsume(), log, S2CItemMsg.ItemList.newBuilder());

        building.setState(BuildingConstant.BUIDING_STATE_UPGRADE).setFinishTime(
                getTargetTime(cfg.getNeedTime()));
        building.update();

        player.getPlayerProxy().startSecondsJob();

        BuildingInfo buildingInfo = BuildingMsgBuilder.buildBuildingInfoMsg(building);
        org.game.protobuf.s2c.S2CBuildingMsg.BuildingUpgradeMsg msg =
                S2CBuildingMsg.BuildingUpgradeMsg.newBuilder().setBuildingInfo(buildingInfo)
                        .build();

        LogDsp logDsp =
                LogDsp.newBuilder(BuildingDsp.BEGIN_UPGRADE).addAttr("nowLvl", cfg.getLevel());
        LogBuilding.me().log(player.getId(), cfg.getBuildId(), logDsp);
        return ProtoUnit.toByte(msg);
    }

    @Override
    public byte[] getWorkReward(ISession session, GetWorkRewardMsg getWorkRewardMsg)
            throws AbstractLogicModelException {
        Player player = session.getAttr(SessionConstent.PLAYER);
        int cid = getWorkRewardMsg.getBuidingCid();

        Building building = BuildingCache.me().getByPlayerIdCid(player.getId(), cid);
        ToolError.isAndTrue(GameErrorCode.CLIENT_PARAM_IS_ERR, building == null);
        ToolError.isAndTrue(GameErrorCode.BUILDING_IS_NOT_GAIN_STATE, "非收获状态，不能收获 ",
                building.getState() != BuildingConstant.BUIDING_STATE_WORK_FINISH);

        int buildingId = building.getBuildingProxy().getCfg().getBuildId();

        LogDsp log = LogDsp.newBuilder(GoodsDsp.WORK_REWARD).targetTag(buildingId);

        List<RewardsMsg> rewards = ItemManager.unpackNotify(player, building.getReward(), log);

        // 更新hero状态
        List<Role> updateHeroList = Lists.newArrayList();
        List<Long> roleIds = building.getRoleId();
        roleIds.forEach(roleId -> {
            Role role = RoleCache.me().getById(roleId);
            role.setWorkBuildingId(0);
            role.update();
            updateHeroList.add(role);
        });
        // player.sendMsg(MessageManager.me().create(HeroBO.sendHeros,
        // ProtoUnit.toByte(HeroMsgBuilder.createHeroInfoList(S2CShareMsg.ChangeType.UPDATE,
        // updateHeroList))));

        building.getBuildingProxy().clear();

        Map<String, Object> in = Maps.newHashMap();
        in.put(EventConditionKey.CONDITION_TYPE, EventConditionType.RECEIVE_WORK_REWARD_COUNT);
        in.put(EventConditionKey.BUILD_CID, buildingId);
        GameEvent event = new GameEvent(player, EventType.BUILDING_EVENT, in);
        GameEventPlugin.syncSubmit(event);

        BuildCfg buildingCfg = building.getBuildingProxy().getNextLevelCfg();
        LogDsp logDsp =
                LogDsp.newBuilder(BuildingDsp.COLLECT_SALARY).addAttr("nowLvl",
                        buildingCfg.getLevel());
        LogBuilding.me().log(player.getId(), buildingId, logDsp);

        return ProtoUnit.toByte(BuildingMsgBuilder.getWorkRewardMsg(building, rewards));
    }

    @Override
    public byte[] enterCity(ISession session, C2SBuildingMsg.EnterCity enterCity)
            throws AbstractLogicModelException {

        Player player = session.getAttr(SessionConstent.PLAYER);

        CityCfg cityCfg = CityCfgCache.me().getById(enterCity.getCityCid());

        unlockCity(player,cityCfg);

        // 检查拥有道具
        Map possessItems =
                ToolMap.getMap(BuildingConstant.ENTER_CITY_CHECK_TYPE_HAVE_ITEM,
                        cityCfg.getNeedItem());
        ToolError.isAndTrue(GameErrorCode.ITEM_NOT_ENOUGH, "道具不足",
                !ItemManager.checkGoodsByCid(player, possessItems));

        // 消耗道具
        Map costItems =
                ToolMap.getMap(BuildingConstant.ENTER_CITY_CHECK_TYPE_COST_ITEM,
                        cityCfg.getNeedItem());
        ToolError.isAndTrue(GameErrorCode.ITEM_NOT_ENOUGH, "道具不足",
                !ItemManager.checkGoodsByCid(player, costItems));


        LogDsp log = LogDsp.newBuilder(GoodsDsp.ENTER_CITY).targetTag(enterCity.getCityCid());

        // 扣除道具
        ItemManager.costGoodsByCid(player, costItems, log, null);

        return new byte[0];
    }

    // **********************private method**********************//

    /**
     * 校验建筑是否已解锁
     *
     * @param player 玩家对象
     * @param buildingId 建筑cid
     */
    private void checkBuildingIsUnlock(Player player, int buildingId) {
        Building building = BuildingCache.me().getByPlayerIdCid(player.getId(), buildingId);
        ToolError.isAndTrue(GameErrorCode.BUILDING_IS_UNLOCK, building != null);
    }

    /**
     * 校验建筑是否尚未解锁
     *
     * @param player 玩家对象
     * @param cid 建筑cid
     */
    private void checkBuildingIsNotUnlock(Player player, int cid) {
        Building building = BuildingCache.me().getByPlayerIdCid(player.getId(), cid);
        ToolError.isAndTrue(GameErrorCode.BUILDING_IS_NOT_UNLOCK, building == null);
    }

    /**
     * 校验建筑是否空闲
     *
     * @param building 建筑对象
     */
    private void checkBuildingIsFree(Building building) {
        ToolError.isAndTrue(GameErrorCode.BUILDING_IS_NOT_FREE,
                BuildingConstant.BUIDING_STATE_FREE != building.getState());
    }

    /**
     * 检查建筑解锁条件
     *
     * @param player 玩家
     * @param buildCfg 建筑配置
     */
    private void checkBuildUnlockCondition(Player player, BuildCfg buildCfg) {
        Integer needLevel =
                (Integer) buildCfg.getOpenType().get(BuildingConstant.UNLOCK_CONDITION_TYPE_LEVEL);
        // 检查玩家等级
        ToolError.isAndTrue(GameErrorCode.CAN_NOT_UNLOCK_PLAYER_LEVEL,
                needLevel != null && player.getLevel() < needLevel);
        Integer chapterCid =
                (Integer) buildCfg.getOpenType()
                        .get(BuildingConstant.UNLOCK_CONDITION_TYPE_CHAPTER);
        if (chapterCid != null) {
            DungeonLevel dungeonLevel =
                    DungeonLevelCache.me().getDungeonLevelCacheProxy()
                            .getByPlayerIdCid(player.getId(), chapterCid);
            ToolError.isAndTrue(GameErrorCode.CAN_NOT_UNLOCK_DUNGEON, dungeonLevel == null);
        }
    }

    /**
     * 获得结束时间（升级结束时间、打工结束时间）
     *
     * @param second 秒
     */
    private Date getTargetTime(int second) {
        return ToolDate.addSecond(second, new Date());
    }

    /**
     * 校验打工规则
     *
     * @param player 玩家对象
     * @param building 建筑对象
     * @param roleIdList 精灵id列表
     */
    private void checkWorkRule(Player player, Building building, List<Long> roleIdList) {

        Set<Long> set = Sets.newHashSet();
        set.addAll(roleIdList);
        // 同一精灵的Id传了多次
        ToolError.isAndTrue(GameErrorCode.CLIENT_PARAM_IS_ERR, set.size() < roleIdList.size());

        BuildCfg cfg = building.getBuildingProxy().getCfg();
        // 打工要求
        Map<Integer, Object> demandMap = cfg.getDemand();

        for (Map.Entry<Integer, Object> entry : demandMap.entrySet()) {
            switch (entry.getKey()) {
            // 打工要求：至少派遣N个精灵
                case BuildingConstant.WORK_CONDITION_1:
                    int param = ToolMap.getInt(entry.getKey(), demandMap);
                    ToolError.isAndTrue(GameErrorCode.WORK_CONDITION_1, roleIdList.size() < param);
                    break;
                // 打工要求：队伍中必须包含某个精灵
                case BuildingConstant.WORK_CONDITION_2:
                    int needRoleId = ToolMap.getInt(entry.getKey(), demandMap);
                    boolean haveRole = false;
                    for (Long roleId : roleIdList) {
                        Role role = RoleCache.me().getById(roleId);
                        if (role.getCid() == needRoleId) {
                            haveRole = true;
                            break;
                        }
                    }
                    ToolError.isAndTrue(GameErrorCode.WORK_CONDITION_2, !haveRole);
                    break;
                // 打工要求：队伍中精灵好感度达到X
                case BuildingConstant.WORK_CONDITION_3:
                    int needFavor = ToolMap.getInt(entry.getKey(), demandMap);
                    roleIdList.forEach(roleId -> {
                        Role role = RoleCache.me().getById(Long.valueOf(roleId));
                        ToolError.isAndTrue(GameErrorCode.WORK_CONDITION_3,
                                role.getFavor() < needFavor);
                    });
                    break;
                // 打工要求：队伍中精灵心情值达到X
                case BuildingConstant.WORK_CONDITION_4:
                    int needMood = ToolMap.getInt(entry.getKey(), demandMap);
                    roleIdList.forEach(roleId -> {
                        Role role = RoleCache.me().getById(Long.valueOf(roleId));
                        ToolError.isAndTrue(GameErrorCode.WORK_CONDITION_3,
                                role.getMood() < needMood);
                    });
                    break;
                default:
                    break;
            }
        }
    }

    /**
     * 校验精灵工作状态
     *
     * @param player 玩家对象
     * @param roleIdList 精灵id列表
     */
    private void checkRoleWorkState(Player player, List<Long> roleIdList) {
        List<Building> buildingList = player.getBuildingsFkPlayerId();
        buildingList.forEach(building -> building.getRoleId().forEach(roleId -> {
            ToolError.isAndTrue(GameErrorCode.ROLE_IS_WORKING, roleIdList.contains(roleId));
        }));
    }

    /**
     * 校验精灵id
     */
    private void checkRoleId(List<Long> roleIdList) {
        roleIdList.forEach(roleId -> {
            Role role = RoleCache.me().getById(roleId);
            ToolError.isAndTrue(GameErrorCode.ROLE_ID_IS_ERR, role == null);
        });
    }

    /**
     * 检查打工者人数
     *
     * @param building 建筑
     * @param roleIdList 打工者id列表
     */
    private void checkWorkerNum(Building building, List<Long> roleIdList) {
        BuildCfg buildCfg = building.getBuildingProxy().getCfg();
        ToolError.isAndTrue(GameErrorCode.WORK_NUM_IS_TOO_MUCH,
                buildCfg.getPosition() < roleIdList.size());
    }

    /**
     * 检查建筑升级条件
     *
     * @param player 玩家
     * @param cfg 建筑配置
     */
    private void checkBuildingUpGradeCondition(Player player, BuildCfg cfg) {
        if (cfg.getUpType() == null) {

            Integer conditionPlayerLevel =
                    (Integer) cfg.getUpType().get(BuildingConstant.BUILDING_UPGRADE_CONDITION_PLV);
            Integer conditionChapterId =
                    (Integer) cfg.getUpType().get(
                            BuildingConstant.BUILDING_UPGRADE_CONDITION_CHAPTER);

            if (conditionPlayerLevel != null) {
                ToolError.isAndTrue(GameErrorCode.CAN_NOT_UPGRADE_BUILDING_PLAYER_LEVEL,
                        player.getLevel() < conditionPlayerLevel);
            }
            if (conditionChapterId != null) {
                DungeonLevel dungeonLevel =
                        DungeonLevelCache.me().getDungeonLevelCacheProxy()
                                .getByPlayerIdCid(player.getId(), conditionChapterId);
                ToolError.isAndTrue(GameErrorCode.CAN_NOT_UPGRADE_BUILDING_DUNGEON_LEVLE,
                        dungeonLevel == null);
            }
        }
    }

    @Override
    public byte[] upgradeFul(ISession session, ReqUpgradeFul req)
            throws AbstractLogicModelException {
        Player player = session.getAttr(SessionConstent.PLAYER);
        int cid = req.getBuidingCid();
        Building building = BuildingCache.me().getByPlayerIdCid(player.getId(), cid);
        ToolError.isAndTrue(GameErrorCode.CLIENT_PARAM_IS_ERR, building == null);

        building.setState(BuildingConstant.BUIDING_STATE_FREE);
        building.update();

        UpdateBuildingInfo updateBuildingInfo =
                BuildingMsgBuilder.buildUpdateBuildingInfoMsg(building,
                        S2CBuildingMsg.BuildingEvent.UPGRADE_FINISH);
        player.getPlayerProxy().sendMsg(
                MessageManager.me().create(BuildingBO.updateBuilding,
                        ProtoUnit.toByte(updateBuildingInfo)));

        return new byte[0];
    }

    @Override
    public byte[] reqGetGashaponInfo(ISession session, ReqGetGashaponInfo req)
            throws AbstractLogicModelException {
        Player player = session.getAttr(SessionConstent.PLAYER);
        PlayerDataProxy pdp = player.getPlayerDatasFkPlayerId().getPlayerDataProxy();
        if (pdp.getGashaponVO().getPoolId() == 0) {
            // 首次进入，刷新蛋池
            resetGashaponPool(pdp);
        }
        return ProtoUnit.toByte(BuildingMsgBuilder.buildGashaponInfoMsg(pdp));
    }

    @Override
    public byte[] reqStartGashapon(ISession session, ReqStartGashapon req)
            throws AbstractLogicModelException {
        Player player = session.getAttr(SessionConstent.PLAYER);
        // 检查对应建筑是否解锁
        // Building building =
        // BuildingCache.me().getByPlayerIdCid(player.getId(),
        // BuildingConstant.GASHAPON_BUILDIND_ID);
        // ToolError.isAndTrue(GameErrorCode.CLIENT_PARAM_IS_ERR, building == null);
        PlayerDataProxy pdp = player.getPlayerDatasFkPlayerId().getPlayerDataProxy();
        GashaponVO gashaponVO = pdp.getGashaponVO();
        // 检查门票次数
        DiscreteDataCfg cfg = DiscreteDataCfgCache.me().getById(DiscreteDataID.GASHAPON);
        int ticket = ItemConstantId.DAILY_GASHAPON_COUNT;
        int nowNum = player.getPlayerProxy().getGoodsNumByCid(ticket);
        ToolError.isAndTrue(GameErrorCode.GASHAPON_COUNT_OVER, nowNum <= 0);
        // 检查CD
        long now = System.currentTimeMillis();
        // ToolError.isAndTrue(GameErrorCode.GASHAPON_CD, now < gashaponVO.getEndTime());
        // 扣除门票次数
        LogDsp log = LogDsp.newBuilder(GoodsDsp.CMD);
        ItemManager.costGoodsByCid(player, MapUtil.of(ticket, 1), log,
                S2CItemMsg.ItemList.newBuilder());
        int cd = ToolMap.getInt("time", cfg.getData(), 20) * 1000;
        long endTime = now + cd;
        gashaponVO.setEndTime(endTime);
        pdp.save();
        // 玩小游戏事件
        Map<String, Object> in = Maps.newHashMap();
        in.put(EventConditionKey.CONDITION_TYPE, EventConditionType.SMALL_GAME);
        in.put(EventConditionKey.SUB_TYPE, SmallGameConstant.TYPE_GASHAPON);
        GameEvent event = new GameEvent(player, EventType.OTHER_EVENT, in);
        GameEventPlugin.syncSubmit(event);
        return ProtoUnit.toByte(BuildingMsgBuilder.buildRespStartGashaponMsg(endTime));
    }

    @Override
    public byte[] reqCheckGashaponResult(ISession session, ReqCheckGashaponResult req)
            throws AbstractLogicModelException {
        Player player = session.getAttr(SessionConstent.PLAYER);
        PlayerDataProxy pdp = player.getPlayerDatasFkPlayerId().getPlayerDataProxy();
        GashaponVO gashaponVO = pdp.getGashaponVO();
        int dis = 5000;// 容错时间
        // 超时检查
        ToolError.isAndTrue(GameErrorCode.GASHAPON_TIMEOUT,System.currentTimeMillis() > gashaponVO.getEndTime() + dis);
        String pool = gashaponVO.getPool();
        JSONArray arr = JSONArray.parseArray(pool);
        // 抓到的蛋
        List<Integer> cids = req.getCidsList();
        List<Integer> eggIds = new ArrayList<Integer>();
        cids.forEach(cid -> {
            int line = cid / 100 - 1;
            JSONArray lineArr = arr.getJSONArray(line);
            String eggStr = null;
            if(lineArr!=null){
                for (int i = 0; i < lineArr.size(); i++) {
                    if (lineArr.getString(i).startsWith(String.valueOf(cid))) {
                        eggStr = lineArr.getString(i);
                        lineArr.remove(i);
                        break;
                    }
                }
                if (eggStr != null) {
                    eggIds.add(Integer.parseInt(StringUtils.split(eggStr, "_")[1]));
                }
            }
        });
        // 封装掉落信息
        List<RewardsMsg> rewards = Lists.newArrayList();
        if(eggIds.size()>0){
            // 发放奖励
            LogDsp logDsp = LogDsp.newBuilder(GoodsDsp.GASHAPON).addAttr("eggIds", eggIds);
            Map<Integer, Integer> out = new HashMap<Integer, Integer>();
            eggIds.forEach(id -> {
                ItemPackageHelper.unpack(EggCfgCache.me().getById(id).getUseProfit(), null, out);
            });
            // 添加道具
            ItemManager.addGoodsAndPush(player, null, null, out, logDsp);
            out.forEach((k, v) -> {
                rewards.add(RewardsMsg.newBuilder().setId(k).setNum(v).build());
            });
    		gashaponVO.setPool(arr.toString());
    		gashaponVO.setEndTime(0);
    		pdp.save();
        }
        // 蛋池被抓完后自动刷新蛋池
        boolean isEmpty = true;
        for (Object obj : arr.toArray()) {
            JSONArray lineArr = (JSONArray)obj;
            if(lineArr.size()>0){
                isEmpty = false;
                break;
            }
        }
        if(isEmpty){
            resetGashaponPool(pdp);//自动刷新蛋池
        }
        return ProtoUnit.toByte(BuildingMsgBuilder.buildRespCheckGashaponResultMsg(gashaponVO.getPool(), eggIds, rewards));
    }

    @Override
    public byte[] reqRefreshGashaponPool(ISession session, ReqRefreshGashaponPool req)
            throws AbstractLogicModelException {
        Player player = session.getAttr(SessionConstent.PLAYER);
        PlayerDataProxy pdp = player.getPlayerDatasFkPlayerId().getPlayerDataProxy();
        GashaponVO gashaponVO = pdp.getGashaponVO();
        // 检查CD
        long now = System.currentTimeMillis();
        ToolError.isAndTrue(GameErrorCode.GASHAPON_REFRESH_CD, now < gashaponVO.getRefreshCD());
        DiscreteDataCfg cfg = DiscreteDataCfgCache.me().getById(DiscreteDataID.GASHAPON);
        // CD时间
        long cd = ToolMap.getInt("cd", cfg.getData(), 24) * DateUtils.MILLIS_PER_HOUR;
        long endTime = System.currentTimeMillis() + cd;
        gashaponVO.setRefreshCD(endTime);
        // 刷新蛋池
        resetGashaponPool(pdp);
        pdp.save();
        return ProtoUnit.toByte(BuildingMsgBuilder.buildRespRefreshGashaponPoolMsg(gashaponVO.getPoolId(), gashaponVO.getPool(), endTime));
    }

    /** 重置抓娃娃蛋池 */
    private void resetGashaponPool(PlayerDataProxy pdp) {
    	GashaponVO gashaponVO = pdp.getGashaponVO();
        JSONArray array =
                (JSONArray) DiscreteDataCfgCache.me().getData(DiscreteDataID.GASHAPON, "eggs");
        List<Integer> ids = new ArrayList<Integer>();
        int poolId = pdp.getGashaponVO().getPoolId();
        for (int i = 0; i < array.size(); i++) {
            String str = array.getString(i);
            int id = Integer.parseInt(str);
            if (id != poolId) {
                ids.add(id);
            }
        }
        if(ids.size()==0){
            ids.add(poolId);
        }
        // 新蛋池id
        int newPoolId = ids.get(new Random().nextInt(ids.size()));
        gashaponVO.setPoolId(newPoolId);
        GashaponCfg gcfg = GashaponCfgCache.me().getById(newPoolId);
        // 新蛋池
        JSONArray arr = randomGashaponPool(gcfg.getEggItems(), gcfg.getLineMin(), gcfg.getAllMax());
        gashaponVO.setPool(arr.toString());
        pdp.save();
        
    }

    /** 随机抓娃娃蛋池 */
    private static JSONArray randomGashaponPool(String eggItems, String lineMin, String allMax) {
        String[] lines = StringUtils.split(eggItems, "|");
        JSONArray arr = new JSONArray();
        // 蛋池最多出现
        Map<Integer, Integer> maxMap = new HashMap<Integer, Integer>();
        if (allMax != null && !"".equals(allMax)) {
            String[] allMaxs = StringUtils.split(allMax, "|");
            for (String str : allMaxs) {
                String[] strs = StringUtils.split(str, ":");
                int eggId = Integer.parseInt(strs[0]);
                int maxNum = Integer.parseInt(strs[1]);
                maxMap.put(eggId, maxNum);
            }
        }
        // 每行最少出现
        Map<Integer, Map<Integer, Integer>> lineMinMap =
                new HashMap<Integer, Map<Integer, Integer>>();
        if (lineMin != null && !"".equals(lineMin)) {
            String[] lineMins = StringUtils.split(lineMin, "|");
            for (String str : lineMins) {
                String[] strs = StringUtils.split(str, ":");
                int eggId = Integer.parseInt(strs[0]);
                Integer line = Integer.valueOf(strs[1]);
                int minNum = Integer.parseInt(strs[2]);
                Map<Integer, Integer> map = lineMinMap.get(line);
                if (map == null) {
                    map = new HashMap<Integer, Integer>();
                    lineMinMap.put(line, map);
                }
                map.put(eggId, minNum);
            }
        }
        // 每行按权重抽取
        Map<Integer, Integer> totalMap = new HashMap<Integer, Integer>();
        for (int i = 0; i < lines.length; i++) {
            String linestr = lines[i];
            JSONArray arrLine = new JSONArray();
            arr.add(arrLine);
            String[] line = StringUtils.split(linestr, "+");
            int num = Integer.parseInt(line[0]);
            List<Integer> temp = new ArrayList<Integer>();
            Map<Integer, Integer> lineNumMap = new HashMap<Integer, Integer>();
            for (int j = 0; j < num; j++) {
                int v = Integer.parseInt(CommonUtil.selectOneOfN(line[1]));
                temp.add(v);
                Integer value = lineNumMap.get(v);
                lineNumMap.put(v, value == null ? 1 : value + 1);
            }
            if (lineMinMap.containsKey(i + 1)) {// 本行有最少出现数量限制
                Map<Integer, Integer> tarMap = lineMinMap.get(i + 1);
                for (Integer id : tarMap.keySet()) {
                    int lineNum = lineNumMap.containsKey(id) ? lineNumMap.get(id) : 0;// 本行出现数量
                    int dis = tarMap.get(id) - lineNum;// 与目标数量差值
                    // 数量未达标,倒序替换
                    if (dis > 0) {
                        for (int j = temp.size() - 1; dis > 0 && j >= 0; j--) {
                            if (!tarMap.containsKey(temp.get(j))) {
                                temp.set(j, id);
                                dis--;
                            }
                        }
                    }
                }
            }
            for (int k = 0; k < temp.size(); k++) {
                int id = temp.get(k);
                if (maxMap.containsKey(id) && totalMap.containsKey(id)
                        && totalMap.get(id) >= maxMap.get(id)) {// 已达上限
                    // 重新组合随机抽取池，排除掉已达上限的id
                    String[] ss = StringUtils.split(line[1], ",");
                    String tempStr = null;
                    for (int j = 0; j < ss.length; j++) {
                        String[] tt = StringUtils.split(ss[j], ":");
                        if (maxMap.containsKey(Integer.valueOf(tt[0]))) {
                            continue;
                        }
                        tempStr = tempStr == null ? ss[j] : tempStr + "," + ss[j];
                    }
                    if (tempStr == null) {
                        continue;
                    }
                    id = Integer.parseInt(CommonUtil.selectOneOfN(tempStr));
                }
                int uid = (i + 1) * 100 + (k + 1);// 唯一编号
                arrLine.add(uid + "_" + id);
                Integer value = totalMap.get(id);
                totalMap.put(id, value == null ? 1 : value + 1);
            }
        }
        return arr;
    }

    private void unlockCity(Player player, CityCfg cfg){
        City city = CityCache.me().getByCidPlayerId(cfg.getId(),player.getId());
        if (city == null){
            city = new City(0L,cfg.getId(),false,player.getId());
            city.insert();
        }
    }

    public static void main(String[] args) {
        JSONArray arr =
                randomGashaponPool(
                        "6+70001:10,70002:20,70003:70|6+70001:10,70002:20,70003:70|6+70001:10,70002:20,70003:70|6+70001:10,70002:20,70003:70|6+70001:10,70002:20,70003:70",
                        "70001:1:1|70001:2:1", "70001:3|70002:3");
        System.out.println("----randomGashaponPool=" + arr.toString());
    }

}
